#include "socket.h"

using namespace std;

RawSocket::RawSocket(const char *DEVICE)
{
    struct ifreq ifr;
    struct sockaddr_ll sll;
    struct packet_mreq mr;

    /* Abre o socket */
    this->rs = socket(PF_PACKET, SOCK_RAW, 0);
    if (rs == -1){
        cout << "Erro, abrir socket\n" << endl;
	exit(-1);
    }

    /* Id do dispositivo */
    memset(&ifr, 0, sizeof(struct ifreq));
    strcpy(ifr.ifr_name, DEVICE);
    if (ioctl(rs, SIOCGIFINDEX, &ifr) < 0) {
        cout << "Erro ioctl()" << endl;
	exit(-1);
    }

    /* Associa o dispositivo ao socket */
    memset(&sll,0,sizeof(sll));
    sll.sll_family = PF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex; /* deviceid */
    sll.sll_protocol = htons(ETH_P_ALL); /* When protocol is set to htons(ETH_P_ALL) then all protocols are received */
    if(bind(rs, (struct sockaddr *)&sll, sizeof(sll)) == -1){
        cout << "Erro bind()" << endl;
	exit(-1);
    }

    /* Socket em modo promiscuo */
    memset(&mr,0,sizeof(mr));
    mr.mr_ifindex = ifr.ifr_ifindex;
    mr.mr_type = PACKET_MR_PROMISC;
    if (setsockopt(rs,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&mr,sizeof(mr))== -1) {
        cout << "Erro setsockopt()" << endl;
	exit(-1);
    }
}

int RawSocket::getFileDescriptor()
{
    return this->rs;
}

message * RawSocket::getMessage(int *r)
{
    byte buffer[MAX_MESSAGE_SIZE];
    message *receivedMessage = new message();

    if( recv(this->rs, buffer, MAX_MESSAGE_SIZE, 0) == -1)
    {
        //cerr << "ERROR: while receiving message." << endl;
        *r = 3;
        return NULL;
    }

    *receivedMessage = message(buffer);

    /* Para diferenciar, se é sujeira ou pacote corrompido */
    if ( receivedMessage->getStamp() != STAMP )
    {
        *r = 2;
        return NULL;
    }
    
    if( ! receivedMessage->validateMessage() )
    {
        delete receivedMessage;
        cerr << "ERROR: received message is corrupted." << endl;
        *r = 1;
        return NULL;
    }

    *r = 0;
    return receivedMessage;
}

bool RawSocket::sendMessage(message *msg)
{
    if( ! msg )
        return false;

    if( send(this->rs, msg->getMessage(), MAX_MESSAGE_SIZE, 0) == -1 )
    {
        cerr << "ERROR: while sending message." << endl;
        return false;
    }

    return true;
}
